﻿using System;
using System.Threading;

namespace Noria.Web.MyMSDN.Tasks
{
    internal abstract class NTask
    {
        private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

        private const int timeout = 30 * 1000;

        private Thread worker;
        protected AsyncWaiter Waiter { get; private set; }

        public void Start()
        {
            if (Enabled())
            {
                log.InfoFormat("{0} started.", GetType().Name);

                Waiter = new AsyncWaiter();
                worker = new Thread(PerformJob);
                worker.Start();
            }
            else
            {
                log.WarnFormat("{0} is disabled and therefore will not be started.", GetType().Name);
            }
        }

        public void Stop()
        {
            if (Enabled())
            {
                Waiter.Set();
                log.InfoFormat("Exit event has been signaled for {0} worker", GetType().Name);

                if (!worker.Join(timeout))
                {
                    worker.Abort();
                    log.FatalFormat("{0} worker failed to join and has been aborted.", GetType().Name);
                }
                else
                {
                    log.InfoFormat("{0} worker has terminated normally.", GetType().Name);
                }

                log.InfoFormat("{0} stopped.", GetType().Name);
            }
            else
            {
                log.WarnFormat("{0} is disabled and therefore will not be stopped.", GetType().Name);
            }
        }

        private void PerformJob()
        {
            while (Waiter.IsAlive)
            {
                int sleeping = GetNextOccurrenceSleeping();
                log.DebugFormat("{0} worker waits {1} milliseconds to work on the next job.", GetType().Name, sleeping);

                // sleep until the occurrence
                if (Waiter.Sleep(sleeping))
                {
                    try
                    {
                        Execute();
                    }
                    catch (Exception e)
                    {
                        log.Error(e);
                    }
                }
            }
            log.InfoFormat("Exit event has been signaled, {0} worker is ready to terminate.", GetType().Name);
        }

        protected abstract bool Enabled();
        protected abstract void Execute();
        protected abstract int GetNextOccurrenceSleeping();
    }
}